Skip to content

feat: expo config plugin#223

Merged
hurali97 merged 109 commits intomainfrom
feat/expo-config-plugin
Feb 19, 2026
Merged

feat: expo config plugin#223
hurali97 merged 109 commits intomainfrom
feat/expo-config-plugin

Conversation

@artus9033
Copy link
Collaborator

@artus9033 artus9033 commented Jan 22, 2026

Summary

Our brownfield plugin has previously worked with Expo, but required manual setup. This PR provides an Expo config plugin to automate that, and a demo Expo application for testing. The plugin applies all the setup steps we described in brownfield's docs, automatically, during expo prebuild. Additionally:

  • It scans Expo & friends for transitive dependencies -> filters out ones that are already bundled with our AAR -> leaves only necessary ones -> injects them to both Gradle metadata & Maven POM files for publication
      • this process is a bit more complicated: we mimic Gradle's dep resolution mechanics by storing only top versions in a set-like structure

Demo apps & CI have been adjusted:

  • AndroidApp CI has 2 jobs running in parallel: one packaging RNApp and the other ExpoApp, then each building the corresponding flavor of AndroidApp
  • Similarly we have 2 jobs running for AppleApp

CLI has been adjusted to run the Expo prebuild if expo is both: detected installed & explicitly listed in the package.json so as not to produce false positives in monorepo setups.

Finally, the CLI is updated to work seamlessly with Expo projects.


Docs:

  • Added Expo Integration section

TODO

  • Verify the embedExpoDependencies block that previously took care of embedding all expo modules into the AAR

    • Maybe we can re-use this and only keep the code for evaluating transitive dependencies. This should allow us to reduce the code for the plugin shipped with this PR.
  • Evaluate how to use ReactNativeBrownfield already established APIs with Expo

    • Currently, we add a ReactNativeHostManager class in the XCFramework which takes care of everything for Expo
    • The user is required to use ReactNativeHostManager.loadView and present RN

Screenshots

Android CLI screenshots image image image
Apple app: Expo variant
Simulator.Screen.Recording.-.iPhone.17.Pro.-.2026-02-18.at.14.46.23.mov
Apple app: vanilla variant
Simulator.Screen.Recording.-.iPhone.17.Pro.-.2026-02-18.at.14.54.00.mov
Android app: Expo variant
Screen.Recording.2026-02-18.at.2.39.29.PM.mov
Android app: vanilla variant
Screen.Recording.2026-02-18.at.2.40.20.PM.mov

Test plan

CI green.

@artus9033 artus9033 self-assigned this Jan 22, 2026
@artus9033 artus9033 force-pushed the feat/expo-config-plugin branch from d95f090 to f56b57d Compare January 22, 2026 19:47
@artus9033 artus9033 force-pushed the feat/expo-config-plugin branch from f56b57d to 2afa209 Compare January 23, 2026 09:41
```

This will take care of linking the expo dependencies like `expo-image` to your AAR.
The plugin supports Expo projects out of the box. Publishing the AAR to Maven Local will also publish the Expo dependencies to Maven Local so that they can be resolved when building the brownfield app.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this statement is correct. Yes, expo internally publishes its modules to its own local maven repo BUT when we use this plugin, we embed those expo modules in the AAR. If you unzip the AAR and then unzip classes.jar - you can see that the AAR actually contains those modules.

Image

Copy link
Member

@thymikee thymikee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@hurali97 hurali97 requested a review from Copilot February 19, 2026 12:17
@hurali97 hurali97 merged commit 28b77bc into main Feb 19, 2026
12 of 13 checks passed
@hurali97 hurali97 deleted the feat/expo-config-plugin branch February 19, 2026 12:21
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces first-class Expo support for the React Native Brownfield toolchain by adding an Expo config plugin (to automate native setup during expo prebuild), extending the CLI to detect Expo projects and run prebuild/codegen as needed, updating the Gradle plugin for Expo dependency handling, and adding an Expo demo app plus updated CI road tests.

Changes:

  • Added an Expo config plugin in @callstack/react-native-brownfield that generates/patches native iOS/Android project structures for XCFramework/AAR packaging.
  • Updated the Brownfield CLI to detect Expo projects, run expo prebuild when applicable, and improved Brownie codegen behavior when no stores exist.
  • Added an apps/ExpoApp demo and updated AndroidApp/AppleApp + CI workflows to test Expo and vanilla variants in parallel.

Reviewed changes

Copilot reviewed 150 out of 164 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
tsconfig.json Removes workspace TS project references at repo root.
packages/react-native-brownfield/tsconfig.json Expands TS include set for library sources/package.json.
packages/react-native-brownfield/src/expo-config-plugin/withBrownfield.ts New Expo config plugin entry with config resolution and platform plugin composition.
packages/react-native-brownfield/src/expo-config-plugin/logging.ts Adds plugin-scoped logger with debug gating.
packages/react-native-brownfield/src/expo-config-plugin/index.ts Exposes Expo config plugin default export.
packages/react-native-brownfield/src/expo-config-plugin/app.plugin.ts Adds Expo config plugin entrypoint for app.plugin resolution.
packages/react-native-brownfield/src/expo-config-plugin/errors/SourceModificationError.ts Adds plugin-specific error type for source modifications.
packages/react-native-brownfield/src/expo-config-plugin/types/index.ts Adds type re-exports for plugin configuration surface.
packages/react-native-brownfield/src/expo-config-plugin/types/BrownfieldPluginConfig.ts Defines user config + resolved config types.
packages/react-native-brownfield/src/expo-config-plugin/types/RenderedTemplateFile.ts Defines rendered template file primitive.
packages/react-native-brownfield/src/expo-config-plugin/types/ios/BrownfieldPluginIosConfig.ts Adds iOS plugin config shape and resolved type.
packages/react-native-brownfield/src/expo-config-plugin/types/android/BrownfieldPluginAndroidConfig.ts Adds Android plugin config shape and resolved type.
packages/react-native-brownfield/src/expo-config-plugin/template/engine.ts Adds template renderer for plugin-generated native files.
packages/react-native-brownfield/src/expo-config-plugin/template/ios/FrameworkInterface.swift Template for framework interface exports and bundle lookup.
packages/react-native-brownfield/src/expo-config-plugin/template/ios/Info.plist Template Info.plist for generated framework target.
packages/react-native-brownfield/src/expo-config-plugin/template/ios/PodfileTargetBlock.rb Template Podfile target block for framework target.
packages/react-native-brownfield/src/expo-config-plugin/template/ios/ReactNativeHostManager.swift Template host manager for Expo iOS integration.
packages/react-native-brownfield/src/expo-config-plugin/template/ios/patchExpoPre55.sh Template patch script for Expo SDK <55 ExpoModulesProvider.swift.
packages/react-native-brownfield/src/expo-config-plugin/template/android/build.gradle.kts Template Gradle build for generated AAR module.
packages/react-native-brownfield/src/expo-config-plugin/template/android/gradle.properties Template Gradle properties for generated module.
packages/react-native-brownfield/src/expo-config-plugin/template/android/proguard-rules.pro Template proguard rules placeholder.
packages/react-native-brownfield/src/expo-config-plugin/template/android/AndroidManifest.xml Template AndroidManifest for generated module.
packages/react-native-brownfield/src/expo-config-plugin/template/android/ReactNativeHostManager.kt Template host manager for Expo Android integration.
packages/react-native-brownfield/src/expo-config-plugin/template/android/consumer-rules.pro Template consumer rules placeholder.
packages/react-native-brownfield/src/expo-config-plugin/ios/withBrownfieldIos.ts Implements iOS plugin: Xcode target, Podfile edits, file generation.
packages/react-native-brownfield/src/expo-config-plugin/ios/xcodeHelpers.ts Adds helpers for Xcode target/build phases/build settings.
packages/react-native-brownfield/src/expo-config-plugin/ios/podfileHelpers.ts Adds Podfile modification + (pre-55) phase reordering hook injection.
packages/react-native-brownfield/src/expo-config-plugin/ios/withIosFrameworkFiles.ts Generates iOS framework directory and template files.
packages/react-native-brownfield/src/expo-config-plugin/ios/index.ts Exports iOS plugin helpers.
packages/react-native-brownfield/src/expo-config-plugin/android/withBrownfieldAndroid.ts Implements Android plugin: settings/build.gradle changes + file generation.
packages/react-native-brownfield/src/expo-config-plugin/android/withAndroidModuleFiles.ts Generates Android module files from templates and resolved config.
packages/react-native-brownfield/src/expo-config-plugin/android/gradleHelpers.ts Adds helpers to mutate root build.gradle and settings.gradle.
packages/react-native-brownfield/src/expo-config-plugin/android/constants.ts Defines brownfield Gradle plugin coordinate/version constant.
packages/react-native-brownfield/src/expo-config-plugin/android/index.ts Exports Android plugin helpers.
packages/react-native-brownfield/package.json Bumps version and adds export for ./app.plugin.js.
packages/cli/src/brownie/store-discovery.ts Throws typed NoBrownieStoresError when no stores found.
packages/cli/src/brownie/errors/NoBrownieStoresError.ts Adds typed error for “no stores” case.
packages/cli/src/brownie/helpers/runBrownieCodegenIfApplicable.ts Adds helper to run Brownie codegen only if Brownie is installed.
packages/cli/src/brownie/commands/codegen.ts Handles “no stores” gracefully (log + return instead of hard failure).
packages/cli/src/brownfield/utils/rn-cli.ts Removes old RN CLI project discovery helper file.
packages/cli/src/brownfield/utils/project.ts Adds unified project discovery with Expo augmentation + Expo detection.
packages/cli/src/brownfield/utils/paths.ts Switches to in-place mutation (drops cloneDeep) for relative sourceDir.
packages/cli/src/brownfield/utils/index.ts Removes barrel export for utilities.
packages/cli/src/brownfield/utils/expo.ts Adds expo prebuild runner utility for CLI flows.
packages/cli/src/brownfield/index.ts Removes re-export of removed utils barrel.
packages/cli/src/brownfield/commands/publishAndroid.ts Runs Expo prebuild + Brownie codegen before publish flow.
packages/cli/src/brownfield/commands/packageAndroid.ts Runs Expo prebuild + Brownie codegen before AAR packaging.
packages/cli/src/brownfield/commands/packageIos.ts Runs Expo prebuild + Brownie codegen; adjusts .brownfield paths.
packages/cli/package.json Bumps version and adds @expo/config; bumps rock-js deps; removes lodash.clonedeep.
packages/brownie/tsconfig.json Minor formatting change (trailing comma removal).
packages/brownie/package.json Removes react devDependency (aligns with workspace strategy).
package.json Adds @expo/config-plugins / @expo/config-types; adds signed/unsigned publish scripts.
gradle-plugins/react/gradle/libs.versions.toml Adds versioncompare lib; formatting normalization.
gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/utils/StringMatcher.kt Adds string matching abstraction (literal/regex) for artifact filtering.
gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/utils/StringExtensions.kt Adds String.capitalized() helper.
gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/utils/Extension.kt Removes isExpo flag from Gradle plugin extension.
gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Constants.kt Adds Expo transitive dependency filtering constants + matchers.
gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/VariantTaskProvider.kt Uses capitalized() helper; minor formatting improvements.
gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/VariantProcessor.kt Uses capitalized() helper; formatting improvements.
gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/VariantPackagesProperty.kt Adds suppression for unchecked cast and formatting.
gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/VariantHelper.kt Uses capitalized() helper; formatting; minor type simplification.
gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/ProguardProcessor.kt Uses capitalized() helper; improves output-file coercion formatting.
gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/JNILibsProcessor.kt Uses capitalized() helper; formatting improvements.
gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNSourceSets.kt Adjusts source set wiring; moves autolinking java dir to main; formatting.
gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNBrownfieldPlugin.kt Switches to Expo auto-detection + ExpoPublishingHelper integration.
gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/ProjectConfigurations.kt Uses capitalized() helper for variant naming.
gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/expo/utils/VersionMediatingDependencySet.kt Adds version-mediating set for Expo transitive dependency resolution.
gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/expo/utils/ReflectionUtils.kt Adds reflection proxy helpers for Expo Gradle plugin interoperability.
gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/expo/utils/ExpoProjectionPrimitives.kt Adds projection interfaces for reflected Expo Gradle types.
gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/expo/utils/BrownfieldPrimitives.kt Adds publishing/dependency primitives for Expo publishing logic.
gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/artifacts/ArtifactsResolver.kt Plumbs “hasExpo” flag; minor refactors/formatting.
gradle-plugins/react/brownfield/build.gradle.kts Adds snapshot versioning and skip-signing plumbing; adds versioncompare dep.
gradle-plugins/react/README.md Updates Expo support docs (now automatic).
gradle-plugins/publish-to-maven-local.sh Adds --skip-signing flow and snapshot property wiring.
docs/docs/docs/guides/guidelines.mdx Normalizes JS string quotes in example.
docs/docs/docs/getting-started/quick-start.mdx Links “built-in CLI” reference to CLI docs.
docs/docs/docs/getting-started/introduction.mdx Mentions Expo config plugin in feature list.
docs/docs/docs/getting-started/expo.mdx Adds Expo integration guide and plugin options documentation.
docs/docs/docs/getting-started/examples.mdx Adds ExpoApp and explains flavors/configurations for demo apps.
docs/docs/docs/getting-started/_meta.json Adds Expo page to docs navigation.
apps/TesterIntegrated/package.json Switches to workspace protocol for internal dependencies.
apps/TesterIntegrated/BrownfieldStore.brownie.ts Exports store interfaces (for codegen/module augmentation).
apps/TesterIntegrated/App.tsx Reorders Brownie store import to run earlier.
apps/RNApp/src/utils.ts Extracts random theme utils.
apps/RNApp/src/navigation/RootStack.ts Adds navigation type + stack creation in separate module.
apps/RNApp/src/components/counter/index.tsx Adds placeholder counter component for non-iOS.
apps/RNApp/src/components/counter/index.ios.tsx Adds iOS-specific counter implementation using Brownie store.
apps/RNApp/src/HomeScreen.tsx Splits Home screen into its own module; uses utils + counter.
apps/RNApp/src/App.tsx Adds App wrapper around navigation + store import.
apps/RNApp/package.json Switches to workspace protocol deps; removes unused new-app-screen dep.
apps/RNApp/ios/Podfile.lock Updates pod versions/checksums (Brownie/ReactBrownfield/Yoga).
apps/RNApp/index.js Updates App import path to new src/App.
apps/RNApp/android/build.gradle Uses snapshot brownfield gradle plugin version.
apps/RNApp/android/BrownfieldLib/build.gradle.kts Switches publishing version to snapshot; fixes doc typo in comments.
apps/RNApp/BrownfieldStore.brownie.ts Exports store interfaces (for codegen/module augmentation).
apps/RNApp/App.tsx Removes old monolithic App implementation (moved to src/).
apps/README.md Documents ExpoApp + AndroidApp flavors and AppleApp variants.
apps/ExpoApp/tsconfig.json Adds Expo app TS config.
apps/ExpoApp/scripts/prepare-android-build-gradle-for-ci.ts Patches Expo Android build.gradle to use mavenLocal + snapshot plugin in CI.
apps/ExpoApp/package.json Adds Expo app dependencies and brownfield scripts.
apps/ExpoApp/hooks/use-theme-color.ts Adds theme color hook.
apps/ExpoApp/hooks/use-color-scheme.web.ts Adds hydration-safe web color scheme hook.
apps/ExpoApp/hooks/use-color-scheme.ts Re-exports RN hook for native.
apps/ExpoApp/eslint.config.mjs Adds Expo app ESLint config overrides.
apps/ExpoApp/entry.tsx Adds dual registration: RNApp component and Expo default main.
apps/ExpoApp/constants/theme.ts Adds theme constants/fonts.
apps/ExpoApp/components/ui/icon-symbol.tsx Adds icon mapping for non-iOS platforms.
apps/ExpoApp/components/ui/icon-symbol.ios.tsx Adds iOS symbol icon component.
apps/ExpoApp/components/ui/collapsible.tsx Adds collapsible UI component.
apps/ExpoApp/components/themed-view.tsx Adds themed view component.
apps/ExpoApp/components/themed-text.tsx Adds themed text component.
apps/ExpoApp/components/parallax-scroll-view.tsx Adds parallax scroll view component.
apps/ExpoApp/components/hello-wave.tsx Adds animated hello wave component.
apps/ExpoApp/components/haptic-tab.tsx Adds haptic tab button component.
apps/ExpoApp/components/external-link.tsx Adds external link component with in-app browser behavior.
apps/ExpoApp/components/counter/index.tsx Adds placeholder counter component.
apps/ExpoApp/components/counter/index.ios.tsx Adds iOS-specific counter using Brownie store.
apps/ExpoApp/assets/images/react-logo.png Adds image asset.
apps/ExpoApp/assets/images/partial-react-logo.png Adds image asset.
apps/ExpoApp/assets/images/favicon.png Adds image asset.
apps/ExpoApp/assets/images/android-icon-monochrome.png Adds image asset.
apps/ExpoApp/app/modal.tsx Adds modal route screen.
apps/ExpoApp/app/_layout.tsx Adds app root layout with stack and theme provider.
apps/ExpoApp/app/(tabs)/index.tsx Adds home tab route.
apps/ExpoApp/app/(tabs)/explore.tsx Adds explore tab route.
apps/ExpoApp/app/(tabs)/_layout.tsx Adds tabs layout.
apps/ExpoApp/app.json Adds Expo config including brownfield plugin usage.
apps/ExpoApp/RNApp.tsx Adds simple RN surface used for native embedding.
apps/ExpoApp/README.md Adds default Expo README for the demo app.
apps/ExpoApp/BrownfieldStore.brownie.ts Adds Brownie store module augmentation for Expo app.
apps/ExpoApp/.gitignore Adds ignore rules including generated native folders.
apps/AppleApp/prepareXCFrameworks.js Adds script to copy/rename built XCFrameworks into AppleApp package dir.
apps/AppleApp/package.json Adds scripts for expo/vanilla variants; switches to ESM and adds dev deps.
apps/AppleApp/Brownfield-Apple-App-Info.plist Adds minimal Info.plist file.
apps/AppleApp/.gitignore Ignores generated package/ directory.
apps/AndroidApp/package.json Splits build scripts by flavor (expo/vanilla).
apps/AndroidApp/gradle/libs.versions.toml Adds separate deps for Expo vs vanilla AAR coordinates; adds Material.
apps/AndroidApp/gradle.properties Minor whitespace tweak.
apps/AndroidApp/app/src/vanilla/java/com/callstack/brownfield/android/example/ReactNativeHostManager.kt Adds flavor-specific alias + no-op config-change handler for parity.
apps/AndroidApp/app/src/vanilla/java/com/callstack/brownfield/android/example/ReactNativeConstants.kt Adds shared constant for module name in vanilla flavor.
apps/AndroidApp/app/src/main/res/values/themes.xml Fixes typo in theme name.
apps/AndroidApp/app/src/main/java/com/callstack/brownfield/android/example/MainActivity.kt Wires config changes and uses constant module name.
apps/AndroidApp/app/src/main/AndroidManifest.xml Fixes theme reference name.
apps/AndroidApp/app/src/expo/java/com/callstack/brownfield/android/example/ReactNativeHostManager.kt Adds flavor-specific alias to Expo-generated host manager.
apps/AndroidApp/app/src/expo/java/com/callstack/brownfield/android/example/ReactNativeConstants.kt Adds shared constant for module name in Expo flavor.
apps/AndroidApp/app/build.gradle.kts Adds product flavors and flavor-specific brownfieldlib dependencies.
CONTRIBUTING.md Updates scripts list and adds new consumer build variants.
.github/workflows/ci.yml Splits Android/iOS road tests into expo and vanilla variants using composite actions.
.github/actions/appleapp-road-test/action.yml Adds reusable composite action for AppleApp road tests.
.github/actions/androidapp-road-test/action.yml Adds reusable composite action for AndroidApp road tests.
.changeset/config.json Adds Expo example app package to ignored list.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +41 to +46
) -> UIView {
let bundleURL = reactNativeDelegate?.bundleURL()
return (expoDelegate?.recreateRootView(
withBundleURL: bundleURL, moduleName: moduleName, initialProps: initialProps,
launchOptions: launchOptions))!
}
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loadView force-unwraps the result of expoDelegate?.recreateRootView(...), which will crash if initialize() wasn't called or if Expo returns nil. Consider guarding and throwing/fatalError with a clear message (or returning an optional) to avoid a hard-to-debug runtime crash.

Copilot uses AI. Check for mistakes.
Comment on lines +3 to +5
# Path to ExpoModulesProvider.swift
FILE="${SRCROOT}/Pods/Target Support Files/Pods-ExpoApp-{{FRAMEWORK_NAME}}/ExpoModulesProvider.swift"

Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The patch script hardcodes Pods-ExpoApp-{{FRAMEWORK_NAME}} in the path. For consumers whose app target isn't named ExpoApp, the file won't be found and the patch won't run. Consider parameterizing the app target name (or locating ExpoModulesProvider.swift dynamically) so the plugin works in arbitrary projects.

Copilot uses AI. Check for mistakes.
Comment on lines +108 to +112
export default createRunOncePlugin(
withBrownfield,
process.env.npm_package_name as string,
process.env.npm_package_version as string
);
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createRunOncePlugin is passed process.env.npm_package_name / npm_package_version. Those env vars are not guaranteed to be set in all execution contexts, which can cause the plugin name/version to become undefined and break run-once semantics. Prefer a stable literal (package name) and a version sourced from package.json at build time.

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +44
ios: expoConfig.ios
? {
frameworkName: config.ios?.frameworkName ?? 'BrownfieldLib',
bundleIdentifier:
config.ios?.bundleIdentifier ??
`${expoConfig.ios.bundleIdentifier}.brownfield`,
buildSettings: config.ios?.buildSettings ?? {},
deploymentTarget: config.ios?.deploymentTarget ?? '15.0',
frameworkVersion: config.ios?.frameworkVersion ?? '1',
}
: null,
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bundleIdentifier default uses ${expoConfig.ios.bundleIdentifier}.brownfield, but expoConfig.ios.bundleIdentifier can be undefined. This would silently produce undefined.brownfield. Consider validating and throwing a clear error when the bundle identifier (or android package) is missing.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +14
// arg parser
import yargs from 'yargs';

const { appName } = yargs(process.argv.slice(2))
.usage('prepareXCFrameworks --appName <appName>')
.demandOption('appName', 'App name is required, pass it as an argument')
.parse();

Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file uses yargs, but apps/AppleApp/package.json doesn't declare it. In a workspace install this may not be available and the build script will fail at runtime. Add yargs as a (dev)dependency for AppleApp, or replace it with a minimal process.argv parser.

Suggested change
// arg parser
import yargs from 'yargs';
const { appName } = yargs(process.argv.slice(2))
.usage('prepareXCFrameworks --appName <appName>')
.demandOption('appName', 'App name is required, pass it as an argument')
.parse();
// minimal arg parser to avoid external dependency on "yargs"
function parseArgs(argv) {
let appName;
for (let i = 0; i < argv.length; i++) {
const arg = argv[i];
if (arg === '--appName') {
appName = argv[i + 1];
break;
}
if (arg.startsWith('--appName=')) {
appName = arg.split('=', 2)[1];
break;
}
}
return { appName };
}
const { appName } = parseArgs(process.argv.slice(2));

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +56
export function isExpoProject(projectRoot: string): boolean {
const hasExpoConfig = getExpoConfigIfIsExpo(projectRoot) !== null;

// additionally, it is needed to check if the project depends on Expo packages explicitly
// to prevent false positives in a monorepo setup
const rnProjectRoot = findProjectRoot();
const packageJsonPath = path.join(rnProjectRoot, 'package.json');
const dependsOnExpo =
fs.existsSync(packageJsonPath) &&
['dependencies', 'peerDependencies', 'devDependencies'].some(
(key) => JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))[key]?.expo
);

return hasExpoConfig && dependsOnExpo;
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isExpoProject(projectRoot) calls findProjectRoot() again to locate package.json, ignoring the projectRoot parameter. This can mis-detect Expo when the CLI is invoked from a different working directory or when projectRoot is explicitly provided. Use the passed projectRoot consistently (and parse package.json once instead of re-reading it per dependency key).

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +23
const templatePath = path.join(__dirname, platform, name);

let templateContent = fs.readFileSync(templatePath, 'utf8');

for (const [key, value] of Object.entries(params ?? {})) {
const regex = new RegExp(key, 'g');
templateContent = templateContent.replace(regex, String(value));
}
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renderTemplate builds a RegExp directly from the template key (e.g. {{FRAMEWORK_NAME}}). Curly braces are special in regex, so this can throw at runtime (invalid regex) and break prebuild. Prefer plain string replacement (e.g. split/join or replaceAll) or escape the key before constructing the RegExp. Also consider resolving the template directory in an ESM-safe way (avoid relying on __dirname in the lib/module build).

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +35
public func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
((expoDelegate?.application(application, didFinishLaunchingWithOptions: launchOptions)) != nil)
}
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method returns true whenever expoDelegate?.application(...) is non-nil, even if the delegate returns false. It should return the delegate's Bool result (or a sensible default) rather than checking for nil.

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +20
1. Add the following block to your `app.json` file:

```json
{
"plugins": [
"@callstack/react-native-brownfield",
]
}
```
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The app.json example is not valid Expo config JSON: the plugins array should be nested under the top-level "expo" key, and the snippet currently has an extra trailing comma/bracket. This will confuse users copy/pasting the example.

Copilot uses AI. Check for mistakes.
- `build:example:android-consumer` - builds the example native Android consumer app (`apps/AndroidApp`)
- `build:example:ios-consumer` - builds the example native Apple consumer app (`apps/AppleApp`) No newline at end of file
- `build:example:android-consumer:expo` - builds the example native Android consumer (`apps/AndroidApp`) app's flavor consuming the Expo RN app (`apps/ExpoApp`) artifact
- - `build:example:android-consumer:vanilla` - builds the example native Android consumer (`apps/AndroidApp`) app's flavor consuming the vanilla RN app (`apps/RNApp`) artifact
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This list item is accidentally double-bulleted (- - ...), which breaks markdown formatting. Remove the extra - so it renders as a single bullet.

Suggested change
- - `build:example:android-consumer:vanilla` - builds the example native Android consumer (`apps/AndroidApp`) app's flavor consuming the vanilla RN app (`apps/RNApp`) artifact
- `build:example:android-consumer:vanilla` - builds the example native Android consumer (`apps/AndroidApp`) app's flavor consuming the vanilla RN app (`apps/RNApp`) artifact

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Comments